package retrofit2

import java.util.regex.Pattern

fun <R> mockUrl(call: Call<R>, mockUrl: String): Call<R> {
    val clz = call.javaClass
    try {
        val convertMockUrl = StringBuilder(mockUrl)
        val requestFactory = clz.getDeclaredField("requestFactory")
        requestFactory.isAccessible = true
        val obj = requestFactory.get(call)
        val factoryClz = obj.javaClass
        val relativeUrl = factoryClz.getDeclaredField("relativeUrl")
        relativeUrl.isAccessible = true

        val parameterHandlersField = factoryClz.getDeclaredField("parameterHandlers")
        parameterHandlersField.isAccessible = true
        val parameterHandlers = parameterHandlersField.get(obj)
        val argsField = clz.getDeclaredField("args")
        argsField.isAccessible = true
        val args = argsField.get(call) as Array<*>
        if (parameterHandlers is Array<out Any?>) {
            parameterHandlers.forEachIndexed { index, item ->
                item?.let { handler ->
                    println("  =>> " + handler.javaClass)
                    if (handler is ParameterHandler.QueryMap<*>) {
                        val argParam = args[index] as Map<*, *>
                        argParam.entries.forEach {
                            val matchKey = "[${it.key}]"
                            if (convertMockUrl.contains(matchKey)) {
                                val start = convertMockUrl.indexOf(matchKey)
                                val end = start + matchKey.length
                                convertMockUrl.replace(start, end, it.value.toString())
                            }
                        }
                    } else if (handler is ParameterHandler.Query<*>) {
                        val nameFiled = handler.javaClass.getDeclaredField("name")
                        nameFiled.isAccessible = true
                        val paramName = nameFiled.get(handler).toString()
                        val matchKey = "[${paramName}]"
                        if (convertMockUrl.contains(matchKey)) {
                            val start = convertMockUrl.indexOf(matchKey)
                            val end = start + matchKey.length
                            convertMockUrl.replace(start, end, args[index].toString())
                        }
                    } else if (handler is ParameterHandler.Body<*>) {
                        val argParam = args[index]!!
                        val bodyClz = argParam.javaClass
                        val pattern = Pattern.compile("\\[[^\\]]*\\]")
                        val match = pattern.matcher(convertMockUrl)
                        while (match.find()) {
                            val key = match.group()
                            val matchKey = key.substring(1, key.length - 1)
                            try {
                                val keyField = bodyClz.getDeclaredField(matchKey)
                                println("found key $matchKey")
                                keyField.isAccessible = true
                                val keyValue = keyField.get(argParam)
                                val start = convertMockUrl.indexOf(key)
                                val end = start + key.length
                                convertMockUrl.replace(start, end, keyValue.toString())
                            } catch (noFiledEx: NoSuchFieldException) {
                                println("could not find $matchKey")
                            }
                        }
                    }
                }
            }
        }
        relativeUrl.set(obj, convertMockUrl.toString())
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return call
}